home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / nidleout < prev    next >
Encoding:
Korn shell script  |  1997-08-26  |  46.3 KB  |  1,290 lines

  1. #!/bin/ksh
  2. # nidleout: new idleout 
  3. # @(#) nidleout.ksh 3.5 97/06/18
  4. # 91/10/13 John H. DuBois III (john@armory.com)
  5. # 91/10/20 Made it also check time since last access, using l -u.
  6. #          Both time since last access and time since last modify must be 
  7. #          greater than the idleout time for a user to be idled out.
  8. #          This fixes the problem where the user is in an application that 
  9. #          relies entirely on the line discipline for tty output,
  10. #          and never does a write itself (ex in insert mode, mail in compose 
  11. #          mode), so that the tty modify time is never updated.
  12. # 91/11/27 Changed modem tty pattern examples to *[A-Z]* from *[A-Z].
  13. # 92/02/08 Changed format of IDLETIME to be a list of user patterns and idle
  14. #          times, so some users can be given longer idle times than others.
  15. # 92/03/30 Do a ps on ttys for logfile *before* killing off processes.
  16. # 92/04/03 Added '... to <user>' to iwrite message
  17. #          eval the test '[[ $user = $pat ]]'; required by new ksh
  18. # 92/05/12 Added -r option
  19. #          Changed IDLETIME to IDLETIMES because its format is different.
  20. # 92/05/13 Added SAVEPROCS
  21. # 93/04/06 Don't do a ps if SAVEPROCS is empty
  22. #          Ignore ttys with line disc 2 (event driver) set.
  23. # 93/04/12 Added -x flag and some debug code.  Let default file values be
  24. #          replaced by internal values by giving empty value on cmd line.
  25. #          Made 60 minute internal default idleout time.
  26. #          Ignore ttys with no line discipline (slip/ppp).
  27. #          Made TTYS able to be a ksh pattern.
  28. #          Give users a minute to clean up.
  29. # 93/04/28 Do not assume PAGER will be set.
  30. # 93/06/02 exec ksh if not running it.
  31. # 93/07/01 Added ability to do a ps on more than 20 ttys
  32. # 93/08/04 Set PATH explicitly.
  33. # 93/11/17 Invoke ps with full path.  Print error if it fails.
  34. # 93/12/31 Added checking for processes using /dev/tty.  Added -A option.
  35. #          Fixed bug that prevented users from being idled out if modify
  36. #          time was 0, regardless of -a.
  37. # 94/03/20 Use " instead of ' around pattern in eval [[ foo = $pattern ]]
  38. # 94/06/17 Fixed bug in code that ignores ttys with no line discipline.
  39. # 94/07/24 New idle time specification format.  Optimize various operations.
  40. #          Kill write if it hangs.  Added  p option.
  41. # 94/09/13 Added NOCHECKDISC.
  42. # 95/01/23 Missed some kills that needed stderr redirected.
  43. # 95/05/13 Added date & time to error messages.
  44. # 95/08/25 Work around ksh bug? by not piping idlettys() into a read statement
  45. # 95/12/27 Test whether utmp entries are still active by kill -0'ing their
  46. #          procs.
  47. # 95/12/28 More v5 porting: Use who -x if available instead of nwho;
  48. #          work with new time format in ps output.
  49. # 96/06/10 Added SIGLIST.  Use correct field for tty in ps output in ps_t().
  50. # 96/09/05 Use ditty for stty of Digi ttys.  Deal correctly with 5.0 stty
  51. #          output that contains both line discipline number & name.
  52. #          Only force device name to lower case when opening sio ports.
  53. # 97/06/18 Deal with who output that includes comment from inittab
  54.  
  55. # todo: Add a category like 'procs', which require that at least the tty
  56. # modify time be touched regularly, for apps that may need to run for a long
  57. # time w/o user input but which will be constantly updating a status.
  58. # e.g. ftp with 'hash' turned on; lynx
  59. # Also, there are some applications (e.g. WordPerfect) that apparently update
  60. # the access time (only) regularly; may be doing non-blocking reads, or perhaps
  61. # reads interrupted by timers for the sake of file syncs and such?  An option
  62. # to use the modify time instead of the access time for certain procs would
  63. # fix this.  ref#787557
  64.  
  65. # Notes:
  66. # There could be a problem when using -A: if a user was using some utility
  67. # that used the event driver, or which had /dev/tty open, nidleout would
  68. # ignore the tty, but if the user quit the utility & nidleout checked the
  69. # tty before the user typed anything further, it would find that the access
  70. # time should be trusted & it would be old.  But, it appears that the access
  71. # time on ttys is updated not only when returning from a read(), but also
  72. # when entering it(!), so this is not a problem in practice unless this
  73. # behaviour is changed.
  74.  
  75. if [ -z "$SECONDS" ]; then
  76.     exec /bin/ksh -c "$0 $@"
  77. fi
  78.  
  79. alias istrue="test 0 -ne"
  80. alias isfalse="test 0 -eq"
  81.  
  82. # iwrite: write from the idleout daemon to a tty
  83. # Usage: iwrite tty message [user]
  84. function iwrite {
  85.     # Use the same device that user is logged in on, rather than always using
  86.     # the lower case device.  Some serial drivers will only let one device be
  87.     # opened, and this code avoids hanging.
  88.     typeset tty=$1
  89.     typeset touser message=$2 user=$3 msg
  90.  
  91.     if [ -w /dev/$tty ]; then
  92.     [ $# = 3 ] && touser=" to $user"
  93.     msg=\
  94. "\n\r\07\07\07Message from idleout_daemon$touser `date \"+[ %a %h %m %T ]\"` ...
  95. $message\r"
  96.     istrue debug && print -u2 "iwrite: Writing$touser $tty:\n$msg"
  97.     ( print "$msg" > /dev/$tty )&
  98.     sleep 5
  99.     if kill -9 $! 2>/dev/null; then
  100.         ErrMsg "write (pid $!)$touser on $tty hung."
  101.         sleep 1
  102.         kill -9 $! 2>/dev/null &&
  103.         ErrMsg "could not kill write process $!; hung?"
  104.     fi
  105.     fi
  106. }
  107.  
  108. function ErrMsg {
  109.     print -u2 "$(date '+%D %T') $tl_name: $*"
  110. }
  111.  
  112. # Usage: hm2min hrs:min
  113. # Converts the time in hrs:min format to a number of minutes and sets
  114. # hm2min_ret to it.
  115. typeset -i hm2min_ret
  116. function hm2min {
  117.     typeset IFS=:
  118.  
  119.     set -- $1
  120.     hm2min_ret=$1*60+$2
  121. }
  122.     
  123. # Usage: checkaccess ttyname ...
  124. # checkaccess prints a single line listing the times in minutes since the last
  125. # read from each tty completed.  The times are printed in the same order
  126. # as the tty names are given.
  127. function checkaccess {
  128.     cd /dev
  129.     l -u $* | awk '
  130.     BEGIN {
  131.     # make sure major & minor #s are read as two words
  132.     # even if minor # is 3 digits
  133.     FS = "[ ,]+"
  134.     }
  135.  
  136.     {
  137.     if ($9 ~ ":") {
  138.         split($9,hm,":")
  139.         acctimes[$10] = hm[1] * 60 + hm[2]
  140.     }
  141.     else
  142.         acctimes[$10] = 24 * 60
  143.     }
  144.  
  145.     END {
  146.     "date \"+%H %M\"" | getline
  147.     curmin = $1 * 60 + $2
  148.     num = split(ttys,TTYNames)
  149.     for (i = 1; i <= num; i++) {
  150.         idle = curmin - acctimes[TTYNames[i]]
  151.         if (idle < 0)
  152.         idle += 24 * 60
  153.         if (idle > 0)
  154.         idle -= 1
  155.         printf idle " "
  156.     }
  157.     print ""
  158.     }
  159.     ' ttys="$*"
  160. }
  161.  
  162. # Usage: TTYMatch user tty
  163. # Return success if user & tty match a rule, and its time is nonzero
  164. function TTYMatch {
  165.     typeset -i i=0
  166.  
  167.     while [ i -lt NumRules ]; do
  168.     eval [[ \$user = "${R_user[i]}" '&&' \$tty = "${R_tty[i]}" ]] &&
  169.     [ ${R_time[i]} -ne 0 ] && return 0
  170.     ((i+=1))
  171.     done
  172.     return 1
  173. }
  174.  
  175. # Usage: MakeTTYList
  176. # MakeTTYList finds ttys that have been idle at least one minute, 
  177. # that users are logged in on and that match the tty,user pair of a rule.
  178. # It sets the arrays ttys[], acctimes[], modtimes[], and users[],
  179. # starting with index 0, to each tty name, the number of minutes since it has
  180. # been accessed, the number of since it has been modified, and the user logged
  181. # in on it.
  182. #
  183. # Expect who -u ouput to be in this form:
  184. #spcecdt    tty05        Oct 04 01:21   .       84
  185. #rbnhood    tty3F        Oct 04 13:25  0:27   2918  This is an inittab comment  
  186. # When split on space, the fields are:
  187. #   1         2           3  4    5      6     7    8    ...
  188. #rbnhood    tty3F        Oct 04 13:25  0:27   2918  This is an inittab comment
  189. # Expect who -ux ouput to be in this form:
  190. #root       tty01        Dec 26 22:25                       old     706   
  191. #pax        tty1b        Dec 27 21:35                      13:09   3872  comment
  192. #cdk        tty3F        Dec 28 12:20                        .    26533   
  193. #cardinal   ttyp0        Dec 28 12:30 gorn.evolve.com        .    28045
  194. #ssahin     ttyp14       Dec 25 18:42 narwhal.cc.metu.edu.tr 12:34   1152
  195. # When split on space, the fields for lines that include hostname are:
  196. #   1         2           3  4    5         6                7      8
  197. #cardinal   ttyp0        Dec 28 12:30 gorn.evolve.com        .    28045
  198. #
  199. # Determining the difference between an entry with and without remote hostname:
  200. # With:    f6 is a hostname; f7 is old/./#:#; f8 is pid
  201. # Without: f6 is old/./#:#;  f7 is pid;       f8 may be comment
  202. # Among these, the only that is guaranteed to be discernible is f7, which will
  203. # only consist entirely of a string of digits if it is a pid (no-hostname)
  204. typeset -i modtimes acctimes
  205. function MakeTTYList {
  206.     # cannot typeset -i idle because the field may be "." (not idle) or "old"
  207.     typeset idle line user tty x=
  208.     typeset -i minutes i=0 proc
  209.     typeset IFS=' '
  210.  
  211.     unset ttys[*] acctimes[*] modtimes[*] users[*]
  212.     # Get modify times of ttys that have been idle at least 1 minute
  213.     # and are ttys that idleout should act on
  214.     $utmpx && x=x
  215.     who -u$x | while read line; do
  216.     set -- $line
  217.     user=$1 tty=$2
  218.     case "$7" in
  219.     +([0-9]))
  220.         idle=$6 proc=$7
  221.         ;;
  222.     *)
  223.         idle=$7 proc=$8
  224.         SetHost "$tty" "$6"
  225.         ;;
  226.     esac
  227.     # some pty using procs don't clean up after themselves.  This is an
  228.     # easy but not conclusive test for that.
  229.     kill -0 $proc 2>/dev/null 1>&2 || {
  230.         istrue debug &&
  231.         print -u2 "PID $proc for line $tty does not exist."
  232.         continue
  233.     }
  234.     case $idle in
  235.     old) idle=24:00;;
  236.     .) idle=0:00;;    # don't skip these; might be using acc time not mod
  237.     esac
  238.     if TTYMatch $user $tty; then
  239.         hm2min $idle 
  240.         minutes=hm2min_ret
  241.         ttys[i]=$tty
  242.         modtimes[i]=minutes
  243.         users[i]=$user
  244.         let i+=1
  245.         istrue debug &&
  246.         print -u2 "From who -u: $user on $tty idle $minutes minute(s)"
  247.     fi
  248.     done
  249.     istrue debug && [ i -eq 0 ] && print -u2 "MakeTTYList: No idle users."
  250.     [ i -eq 0 ] && return
  251.     # Get tty read times
  252.     # Runs l, awk, date once
  253.     checkaccess ${ttys[*]} | read line
  254.     set -A acctimes $line
  255. }
  256.  
  257. # Usage: TTYIdle maxtime tty-index access_only modify_backup
  258. # A line is idle for as long as it has not been read from or written to,
  259. # unless <access_only> is true, in which case it is idle for as long as
  260. # it has not been read from (last-written time is ignored.)
  261. # If modify_backup is true, the modify time is used if the tty is using the
  262. # event line discipline, or if a process on the tty has /dev/tty open and is
  263. # not stopped.
  264. # ttys are ignored if they have no line discipline.
  265. # This function takes the global arrays
  266. # ttys[], acctimes[], modtimes[], and users[], applies rules for which of
  267. # modtime & acctime should be used for the tty, ignores ttys in certain
  268. # situations, and assigns the result to TTYIdle_ret
  269. typeset -i TTYIdle_ret
  270. function TTYIdle {
  271.     typeset -i ind=$1 maxtime=$2 access_only=$3 modify_backup=$4 LineDisc
  272.     typeset -i minutes atime=${acctimes[ind]} mtime=${modtimes[ind]}
  273.     typeset deb
  274.  
  275.     istrue debug &&
  276.     deb="${ttys[ind]}: atime=$atime,mtime=$mtime: "
  277.  
  278.     # if user is not idle, return
  279.     if [ mtime -eq 0 -a atime -eq 0 ]; then
  280.     TTYIdle_ret=0
  281.     return
  282.     fi
  283.     # if mod & acc time are the same, no need for line disc info to decide
  284.     # between them; and if they are less than allowed time, no need to know
  285.     # if there is any line disc at all, so skip it
  286.     if [[ ( mtime -eq atime ) && ( mtime -lt maxtime ) ]]; then
  287.     TTYIdle_ret=mtime
  288.     return
  289.     fi
  290.     ChkLineDisc ${ttys[ind]}    # Runs stty
  291.     LineDisc=$?
  292.     istrue debug &&
  293.     print -u2 "ChkLineDisc ${ttys[ind]} returned $LineDisc"
  294.     if [ LineDisc -eq 255 ]; then
  295.     istrue debug && print -u2 "$deb No line discipline; skipping."
  296.     minutes=0
  297.     # Set minutes to the lesser of access time & mod time.
  298.     elif [ $atime -le $mtime ]; then
  299.     istrue debug && print -u2 "$deb atime <= mtime; using atime"
  300.     minutes=atime
  301.     else
  302.     istrue debug && deb="${deb}mtime < atime; "
  303.     # If access_only, use access time regardless...
  304.     if istrue access_only; then
  305.         # unless modify_backup, in which case the modify time
  306.         # should be used if a process on the tty has /dev/tty
  307.         # open or the event driver is being used
  308.         if istrue modify_backup; then
  309.         if Not_devtty_user ${ttys[ind]} && [ LineDisc -ne 2 ];
  310.         then
  311.             istrue debug &&
  312.             print -u2 "${deb}trusting/using atime"
  313.             minutes=atime
  314.         else    # /dev/tty open or event driver in use
  315.             istrue debug &&
  316.             print -u2 "${deb}not trusting atime; using mtime"
  317.             minutes=mtime
  318.         fi
  319.         else
  320.         istrue debug && print -u2 "${deb}using atime anyway"
  321.         minutes=atime
  322.         fi
  323.     else
  324.         istrue debug && print -u2 "${deb}using mtime"
  325.         minutes=mtime
  326.     fi
  327.     fi
  328.     TTYIdle_ret=minutes
  329. }
  330.  
  331. # Put a list of all pids that have /dev/tty open in global var devttyUsers,
  332. # in the form of a ksh pattern.
  333. function devtty {
  334.     typeset IFS
  335.  
  336.     set -- $(/etc/fuser /dev/tty 2>/dev/null)
  337.     # Make devttyUsers a ksh pattern
  338.     IFS=\|
  339.     devttyUsers="@($*)"
  340.  
  341.     devttyDone=1
  342. }
  343.  
  344. # Not_devtty_user: check whether a process that has /dev/tty open is running on
  345. # the named tty & is not stopped or a zombie
  346. # Usage: Not_devtty_user ttyname
  347. # Global vars: TTYNames[], PIDs[], devttyUsers, ttyprocsDone, devttyDone
  348. function Not_devtty_user {
  349.     typeset tty=$1
  350.     typeset -i i=0
  351.  
  352.     # Run ttyprocs to get a list of all of the procs on this tty.
  353.     isfalse ttyprocsDone && ttyprocs ${ttys[*]}
  354.     isfalse devttyDone && devtty
  355.     while [ -n "${TTYNames[i]}" ]; do
  356.     if [[ ${TTYNames[i]} = $tty && ${ProcStates[i]} != [ZT] ]] &&
  357.     eval [[ ${PIDs[i]} = $devttyUsers ]]
  358.     then
  359.         istrue debug &&
  360.         print -u2 "Process ${PIDs[i]} has /dev/tty open."
  361.         return 1
  362.     fi
  363.     let i+=1
  364.     done
  365.     return 0
  366. }
  367.  
  368. # NotSaveProc: check whether a protected process is running on a tty.
  369. # Usage: NotSaveProc ttyname process-pattern
  370. # Returns success if no protected processes are on the tty.
  371. # Global vars: ProcNames[], TTYNames[]
  372. function NotSaveProc {
  373.     typeset tty=$1 saveprocs=$2
  374.     typeset -i i=0
  375.  
  376.     [ "$saveprocs" = \* ] && return 1
  377.     isfalse ttyprocsDone && ttyprocs ${ttys[*]}
  378.     while [ -n "${TTYNames[i]}" ]; do
  379.     if [[ ${TTYNames[i]} = $tty && ${ProcStates[i]} != [ZT] ]] &&
  380.     eval [[ ${ProcNames[i]} = "$saveprocs" ]]
  381.     then
  382.         istrue debug &&
  383.         print -u2 "Protected process \"${ProcNames[i]}\" running on $tty"
  384.         return 1
  385.     fi
  386.     let i+=1
  387.     done
  388.     return 0
  389. }
  390.  
  391. # ChkLineDisc: check what line discipline a tty is using.
  392. # Usage: ChkLineDisc ttyname
  393. # Return value: Line discipline number,
  394. # or 255 if no line disc (slip/ppp/event/etc. is being used.
  395. # ptys present a problem because if the master is not open, an attempt to open
  396. # the slave will hang.  Generally, the utmp entry will be removed at the time
  397. # the master side is closed, but a case where it isn't is guaranteed to cause
  398. # nidleout to hang until killed, since the master can't be opened again while
  399. # anything (like nidleout) is waiting on open.
  400. # Therefore, the global nocheckdisc may be set to a pattern describing ttys
  401. # that this function should not attempt to open.  0 is always returned instead.
  402. function ChkLineDisc {
  403.     typeset stty_out line tty=$1
  404.     typeset -l ltty=$tty    # Use lower case port for non-blocking sio open
  405.     typeset -i retval
  406.  
  407.     if [ -n "$nocheckdisc" ] && eval [[ \"$tty\" = "$nocheckdisc" ]]; then
  408.     istrue debug &&
  409.     print -u2 "$tty matches \"$nocheckdisc\"; not checking line disc."
  410.     return 0
  411.     fi
  412.     # Some serial drivers will not allow two devices for the same port to be
  413.     # opened at the same time.  Use ditty if available for Digiboard ports;
  414.     # it will do a non-blocking open.  Only have the tty name to go on to
  415.     # determine if it's a Digi port...
  416.     if [[ "$tty" = tty[A-Z][0-9][0-9] ]] && type ditty > /dev/null; then
  417.     stty_out="$( (ditty -a -n /dev/$tty) 2>/dev/null)"
  418.     else
  419.     stty_out="$( (stty -a < /dev/$ltty) 2>/dev/null)"
  420.     fi
  421.     # Lack of any line discipline indicates that slip or ppp is being used.
  422.     # Line discipline 2 is the event driver.
  423.     if [ -z "$stty_out" ]; then
  424.     print -u2 -- "ChkLineDisc: could not get termio parameters for $ltty"
  425.     return 0
  426.     fi
  427.     if [[ "$stty_out" != *"line ="* ]]; then
  428.     istrue debug && print -u2 "$tty has no line discipline."
  429.     return 255
  430.     else
  431.     line=${stty_out#*line = }
  432.     line=${line%%[!0-9]*}
  433.     if istrue debug; then
  434.         case $line in
  435.         0) ;;
  436.         2) print -u2 "$tty is using line discipline $line (event driver)."
  437.            ;;
  438.         *) print -u2 "$tty is using line discipline $line.";;
  439.         esac
  440.     fi
  441.     return $line
  442.     fi
  443. }
  444.  
  445. # Put the host that pty user is logged in from in Hosts[ptynum]
  446. # Expects nwho output to be in these forms:
  447. # mickah   ttyp7       Jul 23 12:42  (friday.echo.com)
  448. # spcecdt  ttyp9       Jul 23 12:38  (deeptht.armory.com:0)
  449. function FindHosts {
  450.     typeset IFS='     ():' user pty d1 d2 h1 h2 host
  451.  
  452.     unset Hosts[*]
  453.     nwho | while read user pty d1 d2 h1 h2 host; do
  454.     host=${host#\(}    # having ( in IFS doesn't seem to work
  455.     SetHost "$pty" "$host"
  456.     done
  457. }
  458.  
  459. # Usage:
  460. # SetHost tty hostname
  461. # Put the host that pty user is logged in from in Hosts[ptynum]
  462. # Note: the indexing used here assumes that pty names are decimal numbered.
  463. # Could convert non-decimal numbers, but then the decimal ones would have
  464. # different values.  Should really use minor # but too much trouble.
  465. # So non-decimal ptys are ignored.
  466. function SetHost {
  467.     typeset pty=$1 host=$2 ptynum
  468.  
  469.     if [[ "$pty" = ttyp* && -n "$host" ]]; then
  470.     ptynum=${pty#ttyp}
  471.     if [[ "$ptynum" = +([0-9]) ]]; then
  472.         Hosts[$ptynum]=$host
  473.         ((debug)) && 
  474.         print -u2 "User $user on $pty is logged in from $host"
  475.     else
  476.         ((debug)) && print -u2 "Warning: non-decimal pty name '$pty'."
  477.     fi
  478.     fi
  479. }
  480.  
  481. # Under 3.2v4:
  482. #                             Already open
  483. # Open of   Neither    Master&slave    Master only    Slave only
  484. # master    Succeeds    Fails        Fails        Fails
  485. # slave     Hangs    Succeeds    Succeeds    Hangs
  486. #
  487. # Usage: idlettys access_only modify_backup
  488. # idlettys prints one line.
  489. # The first word on the line is the number of minutes until the tty that
  490. # is closest to reaching its idle limit without having reached it will
  491. # reach that limit.
  492. # If there are no ttys idle for a minute or more that have not reached
  493. # their limit, the number printed will be the smallest rule time.
  494. #
  495. # The rest of the words are the indexes in ttys[] of lines that match 
  496. # a rule.
  497. #
  498. # A line is idle for as long as it has not been read from or written too.
  499. #
  500. # Global vars:
  501. # idlettys uses ttys[], ttytimes[], users[], idlettys_ilist, and 
  502. # idlettys_mintime
  503. # R_user, R_tty, R_procs, R_rhost, and R_time
  504. # MinTime is the shortest idleout time for any rule.
  505. typeset -i ttytimes idlettys_mintime
  506. function idlettys {
  507.     typeset access_only=$1 modify_backup=$2
  508.     typeset tty user rhost
  509.     # mindiff is initialized to MinTime, so that even if all idle times are 0,
  510.     # we will wake up after MinTime.
  511.     typeset -i i=0 j diff mins time MaxTime Did_nwho=0 ptynum
  512.     idlettys_ilist=
  513.     idlettys_mintime=MinTime
  514.  
  515.     $utmpx && Did_nwho=1
  516.     while [ i -lt ${#ttys[*]} ]; do
  517.     tty=${ttys[i]}
  518.     user=${users[i]}
  519.     j=0
  520.     # find the highest-time rule that matches the idle user
  521.     MaxTime=0
  522.     while [ j -lt NumRules ]; do
  523.         if eval [[ \$user = "${R_user[j]}" '&&' \$tty = "${R_tty[j]}" ]]
  524.         then
  525.         rhost=${R_rhost[j]}
  526.         if [[ "$tty" = ttyp+([0-9]) && $rhost != \* ]]; then
  527.             ((Did_nwho)) || { FindHosts; Did_nwho=1; }
  528.             ptynum=${tty#ttyp*}
  529.             eval [[ \"\${Hosts[ptynum]}\" = $rhost ]] || {
  530.             let j+=1
  531.             continue
  532.             }
  533.         fi
  534.         # Make sure no protected processes are running on the tty
  535.             if NotSaveProc "$tty" "${R_procs[j]}"; then
  536.             let j+=1
  537.             continue
  538.         fi
  539.         mins=${R_time[j]}
  540.         ((debug)) &&
  541.         print -u2 "$user on $tty matches rule $j (itime=$mins)."
  542.         if [ mins -eq 0 ]; then
  543.             MaxTime=0
  544.             break
  545.         fi
  546.         [ mins -gt MaxTime ] && MaxTime=mins
  547.         fi
  548.         let j+=1
  549.     done
  550.     ((debug)) && print -u2 "$user on $tty: idleout time = $MaxTime"
  551.     # If a rule said this login should not be idled out, or there was no
  552.     # rule for the login, skip it
  553.     if [ MaxTime -gt 0 ]; then
  554.         # Find the allowed idle time for this tty
  555.         TTYIdle $i $MaxTime access_only=$1 modify_backup=$2
  556.         time=TTYIdle_ret
  557.         ((debug)) && print -u2 "ttytimes[$i]=$time"
  558.         ttytimes[i]=time
  559.         ((debug)) && print -u2 "idle time for $tty: $time"
  560.         if [ time -ge MaxTime ]; then
  561.         idlettys_ilist="$idlettys_ilist $i"
  562.         else
  563.         diff=MaxTime-time
  564.         [ diff -lt idlettys_mintime ] && idlettys_mintime=diff
  565.         fi
  566.     fi
  567.     let i+=1
  568.     done
  569.     ((debug)) && print -u2 \
  570. "In idlettys, ttytimes indexes are <$idlettys_ilist>.
  571. Values are <${ttytimes[*]}>"
  572. }
  573.  
  574. # Usage: ttyprocs tty ...
  575. # ttyprocs sets the arrays ProcNames[], PIDs[], TTYNames[], and ProcStates[]
  576. # to the real names, process ids, controlling ttys, and states of all
  577. # of the processes attached to any of the ttys given as arguments.
  578. # Indexes start at 1.
  579. # hmm, this doesn't seem to actually pay attention to its ttyname arguments...
  580. # Under 3.2v4 ps -l output has this form:
  581. #  F S    UID   PID  PPID  C PRI NI  ADDR1  ADDR2  SZ     WCHAN  TTY      TIME CMD
  582. # 20 S      0  1618  1615  3  30 20    9d4    69d  88  f00ef8a0  p0       0:10 ksh
  583. # 20 O      0  9925  1618 36  78 20    366    30d  84            p0       0:00 ps
  584. # Under 5.0 it has this form:
  585. #  F S    UID   PID  PPID  C PRI NI     ADDR   SZ     WCHAN     TTY        TIME CMD
  586. #  0 S      0 26072  1231  0  73  0 fb11d218  144  fb11d218  ttyp17    00:00:00 te
  587. # 20 O      0  5059 26073 14  60  0 fb11d370  192         -  ttyp17    00:00:00 ps
  588. function ttyprocs {
  589.     typeset pid header f s uid pid ppid c pri ni addr args
  590.     typeset -i i=0
  591.     ((debug)) && print -u2 "ttyprocs args: $*"
  592.     unset PIDs ProcNames TTYNames ProcStates
  593.     ps_t -l "$@" | {
  594.     read header
  595.  
  596.     while read f s uid pid ppid c pri ni addr args; do
  597.         ((debug)) && 
  598.         print -u2 "From ps_t -l: $f|$s|$uid|$ppid|$c|$pri|$ni|$addr|$args"
  599.         # zombies lack many fields and are not important, so skip them.
  600.         [ "$2" = Z ] && continue
  601.         ProcStates[i]=$s
  602.         PIDs[i]=$pid
  603.         # Remaining fields are ADDR2 SZ WCHAN TTY TIME CMD
  604.         # or SZ WCHAN TTY TIME CMD
  605.         # addr2 and/or wchan may be missing and/or size may merge into
  606.         # addr2.  Deal with this by shifting until TIME is in field 2
  607.         set -- $args
  608.         while [[ $# -gt 0 && "$2" != ?([0-9][0-9]:)+([0-9]):[0-5][0-9] ]]
  609.         do
  610.         shift
  611.         done
  612.         [ $# -lt 3 ] && continue
  613.  
  614.         [[ "$1" = tty* ]] && TTYNames[i]=$1 || TTYNames[i]=tty$1
  615.         shift 2
  616.         ProcNames[i]="$*"
  617.         ((debug)) && print -u2 \
  618.     "line $((i+1)) from ps -l: State=$s PID=$pid TTY=${TTYNames[i]} Proc=$*"
  619.         let i+=1
  620.     done
  621.     }
  622.     ((debug)) && print -u2 "ttyprocs read $i non-zombie process lines."
  623.     ttyprocsDone=1
  624. }
  625.  
  626. # Usage: ps_t ps-options tty ...
  627. # Pass a null argument if no ps-options
  628. # Equivalent to ps -t, except that tty args are given separately
  629. # Knows where to find tty field for default, -l, and -f output.
  630. function ps_t {
  631.     typeset TTYPat line IFS psopts=$1
  632.  
  633.     shift    # get rid of ps options
  634.     # ps -t has a 20 tty limit but is faster
  635.     if [ $# -le 20 ]; then
  636.     ((debug)) && print -u2 "$0: running /bin/ps $psopts -t $*"
  637.     # Redir from /dev/null in case run with no fd 0, which breaks protlib.
  638.     /bin/ps $psopts -t "$*" < /dev/null || ErrMsg ps failed: errno $ERRNO
  639.     return 0
  640.     fi
  641.  
  642.     # Make TTYPat a ksh pattern
  643.     IFS=\|
  644.     TTYPat="*($*)"
  645.  
  646.     IFS="
  647. "
  648.  
  649.     # This & the for loop are much faster than a read loop
  650.     ((debug)) && print -u2 "$0: running /bin/ps $psopts -e"
  651.     set -- $(/bin/ps $psopts -e < /dev/null || ErrMsg ps failed: errno $ERRNO)
  652.     IFS="     
  653. "
  654.     # print header & get rid of it
  655.     print -- "$1"
  656.     shift
  657.  
  658.     ((debug)) &&
  659.     print -u2 "$0: read $# lines from ps.  Comparing ttys to $TTYPat"
  660.     # eval is neccessary to make TTYPat be treated as a regexp.
  661.     # repeated eval is expensive, so eval the whole loop, quoting
  662.     # everything except TTYPat.
  663. eval '
  664.     for line in "$@"; do
  665.     set -- $line
  666.     case "$psopts" in
  667.     -f) tty=$6;;
  668.     -l) tty=${12};;
  669.     *) tty=$2;;
  670.     esac
  671.     [[ $tty != tty* ]] && tty=tty$tty
  672.     ((debug)) && print -u2 "$0: tty = $tty"
  673.     [[ $tty = '"$TTYPat"' ]] && print -- "$line"
  674.     done
  675. '
  676. }
  677.  
  678. # Usage: ttykill ttyname idlemin [user]
  679. # Global variables: TTYNames[], PIDs[], Signals[], KillSleep[]
  680. function ttykill {
  681.     typeset tty=$1 idlemin=$2 pids signal
  682.     typeset -i i=0 sleep
  683.  
  684.     while [ -n "${TTYNames[i]}" ]; do
  685.     ((debug)) && print -u2 "$0: TTYNames[$i]=${TTYNames[i]}"
  686.     [ ${TTYNames[i]} = "$tty" ] && pids="$pids ${PIDs[i]}"
  687.     let i+=1
  688.     done 
  689.     ((debug)) && print -u2 "pids for $tty: $pids"
  690.     [ -z "$pids" ] && return 0
  691.  
  692.     i=0
  693.     while [ i -lt ${#Signals[*]} ]; do
  694.     signal=${Signals[i]}
  695.     if ((debug)); then
  696.         if [[ "$signal" = +([0-9]) ]]; then
  697.         print -u2 "Sending signal $signal to $pids"
  698.         else
  699.         print -u2 "Sending SIG$signal to $pids"
  700.         fi
  701.     fi
  702.     kill -$signal $pids 2>|/dev/null
  703.     sleep=${KillSleep[i]}
  704.     let i+=1
  705.     if [ i -lt ${#Signals[*]} -a sleep -gt 0 ]; then
  706.         ((debug)) && print -u2 "Sleeping for $sleep seconds."
  707.         sleep $sleep
  708.     fi
  709.     done
  710. }
  711.  
  712. # Usage: idleout <access_only> <modify_backup> <action> <logfile>
  713. # Sends a SIGHUP to processes attached to ttys that match a rule.
  714. # A number is printed that is the minimum number of minutes until another
  715. # tty could match a rule.
  716. function idleout {
  717.     typeset access_only=$1 modify_backup=$2 action=$3 logfile=$4
  718.     typeset tty idlei ttyNames
  719.     typeset -i sleeptime idlemin i
  720.  
  721.     if istrue debug; then
  722.     CurTime
  723.     print -u2 "Time: $CurTime_ret"
  724.     fi
  725.     ttyprocsDone=0
  726.     devttyDone=0
  727.     # Make ttys[], acctimes[], modtimes[], users[]
  728.     MakeTTYList
  729.     idlettys $access_only $modify_backup
  730.     sleeptime=idlettys_mintime
  731.     idlei=$idlettys_ilist
  732.     ((debug)) && print -u2 \
  733.     "In idleout, ttytimes values are <${ttytimes[*]}>"
  734.     if [ -z "$idlei" ]; then
  735.     print $sleeptime
  736.     return 0
  737.     fi
  738.     for i in $idlei; do
  739.     ttyNames="$ttyNames ${ttys[i]}"
  740.     done
  741.     # Run ttyprocs to get a list of processes to kill for each tty.
  742.     isfalse ttyprocsDone && ttyprocs $ttyNames
  743.     istrue debug && print -u2 "ttys to idle out users on:$ttyNames"
  744.  
  745.     # Do a ps on ttys for logfile before killing off processes...
  746.     if [ -n "$logfile" -a -w "$logfile" ]; then
  747.     {
  748.         date +"%y/%m/%d %T"
  749.         ps_t -f $ttyNames | tail +2 
  750.     } >> $logfile
  751.     fi
  752.     for i in $idlei; do
  753.     tty=${ttys[i]} idlemin=${ttytimes[i]} user=${users[i]}
  754.     if [ idlemin -le 0 ]; then
  755.         ErrMsg \
  756.         "Error: $user idle $idlemin minute(s) on $tty; trying to idleout?"
  757.         ErrMsg "ttytimes index is <$i>.  ttytimes[$i] is <${ttytimes[i]}>."
  758.         ErrMsg \
  759.     "ttytimes indexes are <$idlei>.  ttytimes values are <${ttytimes[*]}>"
  760.         continue
  761.     fi
  762.     case $action in
  763.     warn) iwrite $tty \
  764. "\rYou have been idle for more than $idlemin minutes.
  765. \rPlease log out if you aren't going to do anything." $user
  766.     ;;
  767.     kill) iwrite $tty \
  768. "\rYou have been idle for more than $idlemin minutes,
  769. \rso you're being logged off.  You have one minute to clean up.  Goodbye!" $user
  770.     ;;
  771.     esac
  772.     done
  773.  
  774.     ((debug)) && print -u2 "Action: $action"
  775.     if [ $action = kill ]; then
  776.     sleep 60    # Give the user the promised minute to clean up.
  777.     ((debug)) && print -u2 "Grace period expired; killing..."
  778.     for i in $idlei; do
  779.         tty=${ttys[i]} idlemin=${ttytimes[i]} user=${users[i]}
  780.         ((debug)) && print -u2 "+ttykill $tty $idlemin $user"
  781.         ttykill $tty $idlemin $user
  782.     done
  783.     fi
  784.     print $sleeptime
  785. }
  786.  
  787. # MakeIdleArrs: convert a list of user=time specs to rules.
  788. # Usage: MakeIdleArrs pattern=time [ pattern=time ... ]
  789. # MakeIdleArrs returns 0 on success, 1 on failure
  790. function MakeIdleArrs {
  791.  
  792.     for arg; do
  793.     MakeRule "user=${arg%=*}:time=${arg#*=}" || return 1
  794.     done
  795.     return 0
  796. }
  797.  
  798. function main {
  799.     typeset -i sleeptime
  800.  
  801.     while :; do
  802.     idleout $access_only $modify_backup $action $logfile |
  803.     read sleeptime
  804.     istrue debug && print -u2 "$tl_name: Sleeping for $sleeptime minute(s)"
  805.     if [ sleeptime -eq 0 ]; then
  806.         print -u2 "$tl_name: got 0 sleeptime!"
  807.         sleeptime=1
  808.     fi
  809.     sleep $((sleeptime * 60))
  810.     done
  811. }
  812.  
  813. # Usage:
  814. # MakeRule rule
  815. # rule is a set of colon-separated fields.
  816. # Each field is of the form name=value
  817. # Parses rules & stores them in global arrays
  818. # R_user, R_tty, R_procs, R_rhost, R_time, and R_signals,
  819. # starting with index 0.
  820. # The smallest time greater than 0 is put in global MinTime.
  821. # The number of rules is stored in global NumRules
  822. # The return value is 0 on success, 1 if the rule was bad
  823. typeset -i NumRules=0 MinTime=0
  824. function MakeRule {
  825.     typeset OIFS IFS Rule=$1 field var value Fields
  826.     typeset -i Err=0 i=0 NumFields
  827.  
  828.     ((debug)) && print -u2 "rule: $Rule"
  829.     OIFS=$IFS
  830.     IFS=:
  831.     set -A Fields -- $Rule
  832.     IFS=$OIFS
  833.     R_user[NumRules]=\*
  834.     R_tty[NumRules]=\*
  835.     R_procs[NumRules]=\*
  836.     R_rhost[NumRules]=\*
  837.     R_time[NumRules]=
  838. #    R_signals[NumRules]=$signals
  839.     NumFields=${#Fields[*]}
  840.     while [ i -lt NumFields ]; do
  841.     field=${Fields[i]}
  842.     IFS==
  843.     set -- $field
  844.     IFS=$OIFS
  845.     var=$1 value=$2
  846.     case $var in
  847.     time)
  848.         if [[ "$value" != +([0-9]) ]]; then
  849.         print -u2 "$tl_name: Invalid time given in rule:\n$Rule"
  850.         Err=1
  851.         else
  852.         if [ $value -gt 1440 ]; then
  853.             print -u2 "Warning: time '$value' changed to 1 day (1440)."
  854.             value=1440
  855.         fi
  856.         R_time[NumRules]=$value
  857.         [[ (MinTime -eq 0 || $value -lt MinTime) && $value -gt 0 ]] &&
  858.         MinTime=$value
  859.         fi
  860.         ;;
  861.     user|tty|procs)
  862.         eval R_$var[NumRules]=\$value
  863.         ;;
  864.     rhost)
  865.         if ! $utmpx && [ Found_nwho -ne 0 ]; then
  866.         print -u2 \
  867.         "$tl_name: need either who -x or nwho if rhost param is used."
  868.         Err=1
  869.         fi
  870.         eval R_rhost[NumRules]=\$value
  871.         ;;
  872.     *)
  873.         print -u2 \
  874.         "$tl_name: Invalid field '$var' assigned to in rule:\n$Rule"
  875.         Err=1
  876.         ;;
  877.     esac
  878.     ((i+=1))
  879.     done
  880.     if [ ${#R_time[*]} -le NumRules ]; then
  881.     print -u2 "$tl_name: No time given in rule:\n$Rule"
  882.     Err=1
  883.     fi
  884.     ((Err)) || ((NumRules+=1))
  885.     return $Err
  886. }
  887.  
  888. # Usage: ProcDefFile default-file-name
  889. # Sets globals idletimes, log, ttys, saveprocs, siglist
  890. # Uses globals NoRules and debug.
  891. function ProcDefFile {
  892.     typeset DefFile=$1 var
  893.     typeset -l lowvar
  894.     typeset -i Err=0
  895.  
  896.     [ ! -r $DefFile ] && return 0
  897.     while read line; do
  898.     case "$line" in
  899.     *([     ])|\#*)        # comment/space
  900.         ((debug)) && print -u2 "comment/space: $line"
  901.         ;;
  902.     @(IDLETIMES|LOGFILE|TTYPAT|SAVEPROCS|NOCHECKDISC|SIGLIST)=*)
  903.         ((debug)) && print -u2 "simple-var: $line"
  904.         # Assign these values carefully
  905.         var=${line%%=*}
  906.         lowvar=$var
  907.         # Assign values only if empty (not set on command line)
  908.         eval [ -z \"$"$lowvar"\" ] && eval $lowvar=\${line#$var=}
  909.         ;;
  910.     [A-Z]*=*)
  911.         print -u2 \
  912.         "$tl_name: Value assigned to unknown variable in $DefFile:\n$line"
  913.         Err=1
  914.         ;;
  915.     *=*)
  916.         ((NoRules)) || MakeRule "$line" || Err=1
  917.         ;;
  918.     *)
  919.         print -u2 "$tl_name: Bad line in $DefFile:\n$line"
  920.         Err=1
  921.         ;;
  922.     esac
  923.     done < $DefFile
  924.     return $Err
  925. }
  926.  
  927. typeset -i CurMinute_ret
  928. function CurMinute {
  929.     (( CurMinute_ret=(SECONDS/60+StartTime) % 1440 ))
  930. }
  931.  
  932. function CurTime {
  933.     typeset -Z2 Hr Min
  934.  
  935.     CurMinute
  936.     ((Hr=CurMinute_ret/60))
  937.     ((Min=CurMinute_ret%60))
  938.     CurTime_ret=$Hr:$Min
  939. }
  940.  
  941. # StartTime may be negative; doesn't matter if only CurMinute uses it.
  942. typeset -i StartTime
  943. function TimeInit {
  944.     typeset -i hr min
  945.  
  946.     date "+%H %M" | read hr min
  947.     StartTime=hr*60+min-SECONDS/60
  948. }
  949.  
  950. # Usage: MakeSigArrs siglist
  951. typeset -i KillSleep
  952. typeset -u Signals
  953. function MakeSigArrs {
  954.     typeset -i i=0
  955.     typeset siglist=$1
  956.  
  957.     OIFS=$IFS
  958.     IFS=,
  959.     set -- $siglist
  960.     IFS=$OIFS
  961.     while [ $# -ge 1 ]; do
  962.     Signals[i]=$1
  963.     shift
  964.     if [ $# -gt 1 ]; then
  965.         KillSleep[i]=$1 || {
  966.         print -u2 "$tl_name: Bad sleep time: $1.  Exiting."
  967.         exit 1
  968.         }
  969.         shift
  970.     fi
  971.     ((debug)) &&
  972.     print -u2 "siglist pair $i: signal=${Signals[i]} sleep=${KillSleep[i]}"
  973.     let i+=1
  974.     done
  975. }
  976.  
  977. # Start of main program
  978.  
  979. set -o noglob
  980. Err=0
  981. DefFile=/etc/default/nidleout 
  982. typeset -i fgnd=0 access_only=0 modify_backup=0 debug=0 ttyprocsDone NoRules=0
  983. typeset -i NoRun=0
  984. action=kill 
  985. tl_name=${0##*/}
  986.  
  987.  
  988. while getopts :D:t:i:l:s:S:aAdefhnpwx flag; do
  989.     case $flag in
  990.     a)    access_only=1;;
  991.     A)    access_only=1; modify_backup=1;;
  992.     d)  action=log;;
  993.     D)  nocheckdisc=$OPTARG;;
  994.     e)  NoRun=1;;
  995.     f)  fgnd=1;;
  996.     n)    NoRules=1;;
  997.     p)  nocheckdisc="ttyp*";;
  998.     w)  action=warn;;
  999.     x)  debug=1;;
  1000.     t)    ttypat=$OPTARG;;
  1001.     s)    saveprocs=$OPTARG;;
  1002.     S)  siglist=$OPTARG;;
  1003.     i)    idletimes=$OPTARG;;
  1004.     l)    logfile=$OPTARG
  1005.     if ( >> $logfile ) 2>|/dev/null; then
  1006.         :
  1007.     else
  1008.         echo "Cannot write to $logfile."
  1009.         Err=1
  1010.     fi;;
  1011.     h)  ${PAGER:-pg} <<END_HELP
  1012. Usage: $tl_name [-aAdefnpwxh] [-t<tty_pattern>] [-i<idle_times>] [-l<logfile>]
  1013.                 [-s<save_procs>] [-D<tty_pattern>]
  1014.      $tl_name logs out idle users.  Whether a user is idle or not is determined
  1015. by examining the last-modified and last-accessed time on the user's tty.  The
  1016. modify time is updated each time that the terminal is written to.  The access
  1017. time is updated each time the terminal is read from.  The terminal is read from
  1018. each time that it returns a line or character of input (depending on whether it
  1019. is in canonical or non-canonical mode) to a process that has requested input.
  1020. These are imperfect indicators of user activity; there is no facility for
  1021. finding out when the last actual input from a user was received.
  1022.      Most utilities read from and write to the tty in such a way that either
  1023. the access or modify time or both are updated.  However, there are some
  1024. situations in which the modify and/or access time on a tty are not updated even
  1025. though a user is active.  The most common are: use of /dev/tty for input and/or
  1026. output, use of the event driver, extended interaction with the tty driver,
  1027. output via direct video access, use of utilities that do not read input for an
  1028. extended period, and complete bypass of the tty mechanism. Options are provided
  1029. to deal with most of these situations.  The following explanations will help
  1030. you recognize them.
  1031.      Some utilities, like pg(C) and less, access the user's tty input by
  1032. reading the device /dev/tty because the process' standard input may have been
  1033. redirected.  In this case, the access time on /dev/tty will be updated instead
  1034. of the access time on the "real" tty.  Similarly, some utilities write to
  1035. /dev/tty because they are liable to be invoked with their output redirected;
  1036. these will not update the modify time of the real tty.
  1037.       Any process that uses the event driver will prevent the access time on
  1038. the tty from being updated, because the event driver bypasses the normal read
  1039. mechanism.  Utilities that use a mouse, like usemouse(C) and the console X
  1040. server, are examples of utilities that use the event driver.
  1041.      Many utilities rely on the line discipline for echoing keyboard input. The
  1042. line discipline does not update the modify time on the tty when it echos input,
  1043. so it will appear that nothing is being written to the user's tty when in such
  1044. a utility (mail(C), ex(C), etc.)  Xon/xoff flow control are also handled within
  1045. the tty driver.  If a user is reading a large file and pausing the output with
  1046. xoff and xon (^S/^Q), the access time will not be updated even though the user
  1047. is typing the xon/xoff characters.
  1048.      Applications that are running on the console and are using direct video
  1049. memory access (VP/ix, Merge, and applications that produce graphical displays,
  1050. like the console X server) will not update the modify time, because they don't
  1051. write to the tty.
  1052.      Some applications do processing for extended period, and do not read from
  1053. the tty while doing this processing.  Even though a user may be typing, and
  1054. depending on the utility the input may be being echoed by the line discipline,
  1055. since no read is being done the user becomes idle.  An example of this is ftp;
  1056. during a long transfer a user will become idle.
  1057.      Finally, some processes divert serial IO at a low level within the kernel.
  1058. Examples of this are the SLIP and PPP drivers.  These update neither the access
  1059. nor modify time.  Idleout never acts on ttys that have no line discipline, 
  1060. which indicates that a driver of this type in in use.
  1061.      When $tl_name is started, it goes into the background and begins checking
  1062. the times on users' ttys.  If a user has been idle for more than the specified
  1063. time, a message is written to the user's tty warning that the user is about to
  1064. be logged off.  After ten seconds has passed, all processes attached to the tty
  1065. that the user is logged in on are sent a terminate signal.  This allows
  1066. processes that catch the terminate signal to clean up.  After another ten
  1067. seconds has passed, all of the remaining processes are sent a hangup signal, so
  1068. that processes that ignore the terminate signal but pay attention to the hangup
  1069. signal can clean up.  After another ten seconds has passed, all of the
  1070. processes are sent a kill signal, which cannot be ignored.
  1071.      Options:
  1072.      -l sets the name of the log file to which one line is written, in the
  1073. format of ps -f, for each process running on the user's tty at the time the
  1074. user is logged out.  The default is $DefFile.  The log file will
  1075. NOT be written to unless it already exists at the time $tl_name is run.  If an
  1076. explicit log file name is given but the file does not already exist, $tl_name
  1077. will abort with an error.
  1078.      If -d or -w is given, the processes running on an idle tty are logged in
  1079. the logfile but the user is not logged out.  If a user remains idle, the user
  1080. will probably be logged more than once.  If -w is given, the user is warned,
  1081. but is still not logged out.
  1082.      -e causes $tl_name to exit immediately after all initialization is 
  1083. complete (for testing of rules, etc.)  Usually used with -x.
  1084.      -f causes $tl_name to remain in the foreground.
  1085.      -a causes only the access time on a tty to be used for the idle time.  The
  1086. default is to consider the time since the most recent of the access and modify
  1087. times to be the idle time.  In general, the access time (which is the last time
  1088. that input from the user was acted on) is a better indicator of user activity
  1089. than the modify time.  For example, some utilities regularly write to a tty
  1090. even when they are not being used.  If a tty with such a utility running on it
  1091. should be considered idle if no one is typing at it, use -a.  Note that, as
  1092. described above, some processes do not update the access time on the tty even
  1093. when they are reading input from a terminal.  If -a is given, users using these
  1094. utilities are liable to be incorrectly logged out.  Use -s and/or -A to prevent
  1095. this.
  1096.      -A is like -a, except that if the tty is not using the standard line
  1097. discipline, or if a process on the tty has /dev/tty open and is not stopped,
  1098. the modify time is used instead of the access time.  In most cases this is a
  1099. better option to use than -a.  However, even -A will not help the situation
  1100. where a user is using xon/xoff to read a large file, or where a user has begun
  1101. a lengthy operation that will not read any more input from the user until it
  1102. has completed.
  1103.      -n causes any rules given in $DefFile to be ignored.  Simple
  1104. assignments are still read.
  1105.      -D prevents $tl_name from checking the line discipline in effect on any
  1106. ttys that match the pattern given.  To prevent the line discpline from being
  1107. checked for any tty, use -D '*' (a pattern no tty will match).  -D can be used
  1108. if there are ttys that $tl_name should not attempt to open for reading (as is
  1109. neccessary to check the line discipline).
  1110.      -p prevents $tl_name from attempting to determine what line discipline
  1111. is in use on ptys.  Due to pty open behaviour in some releases of the operating
  1112. system, in some circumstances an attempt at opening the slave side will hang.
  1113. Note that if -p is given, users using an alternate line discipline on a pty
  1114. are liable to be incorrectly idled out.  -p is equivalent to -D 'ttyp*'
  1115.      -s is used to give a shell pattern that should match any processes that
  1116. should not be idled out.  If the name of any process running on a tty matches
  1117. the pattern given, the user on that tty will not be idled out (no processes on
  1118. the tty will be killed).  This would typically be used to save processes that
  1119. do not update the access or modify time on a tty, or, if the -a flag is being
  1120. used, processes which may update the modify time but not the access time. 
  1121. Example pattern: "@(pg|sx|sz|sb)".  If a pattern is given on the command
  1122. line, it should be protected from the shell with quotes.
  1123.      -i is used to specify the number of minutes a user may be idle before
  1124. being logged out.  Its format is: userpat=minutes[:userpat=minutes...]  Each
  1125. colon-separated specification gives a shell pattern to compare user names to,
  1126. and the number of minutes that users whose login names match that pattern are
  1127. allowed to be idle.  An example of a simple specification is "*=15".  This
  1128. specifies that the idleout time for all users is 15 minutes.  An example of a
  1129. more complex specification is "@(root|backup)=240:spcecdt=60:*=15".  This
  1130. specifies that root and backup have an idleout time of 4 hours, spcecdt has an
  1131. idleout time of 1 hour, and everyone else has an idleout time of 15 minutes. 
  1132. User names are compared to patterns in the order they are given, and the time
  1133. for the first pattern that a user name matches is used.  The default is "*=60".
  1134. A time of 0 minutes means that no idleout should be done.  If a pattern is
  1135. given on the command line, it should be protected from the shell with quotes.
  1136.      -t is used to give a shell pattern that determines which ttys $tl_name
  1137. acts on.  It should match the file part of the tty name, without the leading
  1138. /dev.  Use "*" to match all ttys.  "*[A-Z]*" would typically be used to only
  1139. match modem ttys.  "!(ttyp*)" will match all real ttys, but allow users on
  1140. psuedo-ttys to be idle.  The default is "*".
  1141.      -S is used to give a list of signals to send to processes to kill them,
  1142. and periods to sleep between sending the signals.  The value has the form:
  1143. <signal>[,<sleeptime>]...
  1144. <signal> is either a signal number or the name of the signal without the
  1145. leading 'SIG', e.g. KILL.  <sleeptime> is the period in seconds to sleep after
  1146. sending the preceding signal before sending the next signal.  The default is
  1147. INT,10,HUP,10,KILL
  1148. This causes a SIGINT to be sent, followed by a sleep of 10 seconds, then a 
  1149. SIGHUP to be sent, followed by a sleep of 10 seconds, and finally a SIGKILL to
  1150. be sent.
  1151.      -x turns on debugging output.  It is written to standard error.
  1152.  
  1153.      More complex actions can be specified in the specification file
  1154. /etc/default/nidleout.  Lines in this file that are blank or begin with # are
  1155. ignored.  Other lines have one of two forms, either
  1156. VARNAME=value
  1157. or
  1158. field=value:field=value:...
  1159. The first form is used to assign values to certain of the parameters that can
  1160. be given on the command line.  The tty pattern, idle times, log file, pattern
  1161. to match ttys that should not have their line discipline checked, and saved
  1162. processes pattern may be set by assigning values to the variables TTYPAT,
  1163. IDLETIMES, LOGFILE, NOCHECKDISC, SAVEPROCS, and SIGLIST
  1164. respectively.  For example:
  1165.  
  1166. IDLETIMES=*=15
  1167. TTYPAT=*[A-Z]*
  1168. NOCHECKDISC=ttyp*
  1169. SAVEPROCS=@(pg|more|ftp)
  1170.  
  1171. will set the idle time, tty pattern, and save processes pattern, and leave LOG
  1172. set to the default.  Values given on the command line override any set in the
  1173. default file.
  1174.      The second form is used specify idleout actions that are dependent on more
  1175. than one parameter.  Each line describes an idleout rule.  The rule consists
  1176. of various parameters that must match a user login in order for that rule to
  1177. match the login.  A rule matches only if all of the parameters given match.
  1178. The idleout time used for each login is the highest time of any rule that
  1179. matches the login.  The parameter fields are user, tty, procs, and rhost.  The
  1180. value of each field is given in ksh pattern syntax.  Any parameters not
  1181. assigned a value in a rule default to '*', so that those parameters always
  1182. match.  The 'time' field gives the idleout time for a rule; every rule must
  1183. have a time field.  Note that values higher than 1440 minutes (1 day) are
  1184. rounded down to that, since that is the highest value 'who' will report.  A
  1185. time field of 0 means that logins matching this rule should not be idled out.  
  1186.      The user and tty fields are matched against the user's login name and the
  1187. tty the user is on.  The procs field will match if any process running on the
  1188. tty matches its value.  The rhost field is matched against the host that a
  1189. remote user is logged in from, for rlogin and telnet connections.  It will be
  1190. matched against the domain name of the host if it has one, else its IP address.
  1191. Use of this parameter requires either that the 'who' command implements the -x
  1192. option (print remote hostnames), or the existence of the 'nwho' program, and is
  1193. only checked for pseudo-ttys regardless of the tty parameter.
  1194. Example:
  1195.  
  1196. # These users are allowed to be idle for 60 minutes on a modem.
  1197. user=@(spcecdt|root|pax):tty=tty3[A-H]:time=60
  1198. # These users are allowed to be idle for 1 day on a pty.
  1199. user=@(spcecdt|root|filbo):tty=ttyp+([0-f]):time=1440
  1200. # Users coming from any host in armory.com will never be idled out.
  1201. tty=ttyp+([0-f]):rhost=*.armory.com:time=0
  1202. # These processes read & write /dev/tty, or don't read input during processing.
  1203. tty=tty@(3[A-H]|p+([0-f])):procs=@(pg|rb|rz|rx|sb|sz|sx|ftp):time=240
  1204. # Idle out general modem users in 10 minutes.
  1205. tty=tty3[A-H]:time=10
  1206. # Idle out net users in 14 hours.
  1207. tty=ttyp+([0-f]):time=840
  1208.  
  1209.      Note that the simpler idleout specifications (IDLETIMES, etc.) other than
  1210. NOCHECKDISC would not normally be used in conjunction with rules.  The -n
  1211. option can be used to skip any rules in $DefFile so that simple specifications
  1212. can be given on the command line.  They are internally converted into rules as
  1213. follows: Each field of IDLETIMES becomes a rule with only user and time set. 
  1214. TTYPAT and SAVEPROCS become rules with only tty or procs set, respectively, and
  1215. with time set to 0; in the case of TTYPAT anything except the tty pattern is
  1216. matched.
  1217.      $tl_name is a Korn shell script.  If it is not invoked from a Korn shell,
  1218. and hashpling is not enabled on the system, it must be run by the Korn shell
  1219. explicitly.  If $tl_name is put in /usr/bin, use the command:
  1220. /bin/ksh $0 <arguments...>
  1221. END_HELP
  1222.     exit 0;;
  1223.     ?)    echo "$OPTARG: unknown flag.  Use -h for help."
  1224.     Err=1;;
  1225.     :)    echo "$OPTARG: option requires a value.  Use -h for help."
  1226.     Err=1;;
  1227.     esac
  1228. done
  1229.  
  1230. # External commands used:
  1231. # /bin/ksh awk date who stty ps sleep tail fuser nwho
  1232. export PATH=/bin:/usr/bin:/usr/local/bin
  1233.  
  1234. if who -x >/dev/null 2>&1; then
  1235.     utmpx=true
  1236.     Found_nwho=1
  1237. else
  1238.     utmpx=false
  1239.     whence nwho > /dev/null
  1240.     Found_nwho=$?
  1241. fi
  1242.  
  1243. ProcDefFile $DefFile || Err=1
  1244.  
  1245. if [ $Err = 1 ]; then
  1246.     echo "Exiting."
  1247.     exit 1
  1248. fi
  1249.  
  1250. # Convert simple specs to arrays
  1251. OIFS=$IFS
  1252. IFS=:
  1253. MakeIdleArrs $idletimes || exit 1
  1254. IFS=$OIFS
  1255. [ -n "$ttypat" ] && MakeRule "tty=!($ttypat):time=0"
  1256. [ -n "$saveprocs" ] && MakeRule "procs=$saveprocs:time=0"
  1257. # If no inclusion rules at all given, set this one
  1258. [ MinTime -eq 0 ] && MakeRule "time=60"
  1259. [ -z "$siglist" ] && siglist=INT,10,HUP,10,KILL
  1260. MakeSigArrs "$siglist"
  1261.  
  1262. if ((debug)); then
  1263.     typeset -i i=0
  1264.     while [ i -lt NumRules ]; do
  1265.     print -u2 "Rule $i: user=${R_user[i]}  tty=${R_tty[i]}"\
  1266.     " procs=${R_procs[i]}  rhost=${R_rhost[i]}  time=${R_time[i]}"
  1267.     ((i+=1))
  1268.     done
  1269.     print -u2 "nocheckdisc=$nocheckdisc"
  1270.     print -u2 "siglist=$siglist"
  1271. fi
  1272.  
  1273. trap "" HUP
  1274.  
  1275. if [ -n "$logfile" ]; then
  1276.     echo "$tl_name: logfile is $logfile.  $NumRules rule(s)."
  1277. else
  1278.     echo "$tl_name: $NumRules rule(s)."
  1279. fi
  1280.  
  1281. TimeInit
  1282.  
  1283. ((NoRun)) && exit 0
  1284.  
  1285. if [ $fgnd = 1 ]; then
  1286.     main
  1287. else
  1288.     main &
  1289. fi
  1290.